package com.efivestar.idm.api.service;

import com.efivestar.idm.api.exception.ErrorParamException;
import com.efivestar.idm.api.exception.NotSupportMethodHandlerException;
import com.efivestar.idm.api.service.lock.protocol.handler.LockProtocolHanderlIntf;
import com.efivestar.idm.api.util.ByteUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

/**
 * Created by Lovell on 9/29/16.
 */
/*
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @@@@  @@@@@@@@        @@  @@@@@@@  @@        @@  @@@@@@@@@  @@@@@@@@@@
 @@@@  @@@@@@@@  @@@@  @@@  @@@@@  @@@  @@@@@@@@  @@@@@@@@@  @@@@@@@@@@
 @@@@  @@@@@@@@  @@@@  @@@@  @@@  @@@@       @@@  @@@@@@@@@  @@@@@@@@@@
 @@@@  @@@@@@@@  @@@@  @@@@@  @  @@@@@  @@@@@@@@  @@@@@@@@@  @@@@@@@@@@
 @@@@        @@        @@@@@@   @@@@@@        @@        @@@        @@@@
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 */
// @Component
public class UdpServer implements ApplicationContextAware {
    private static Logger logger = LoggerFactory.getLogger(UdpServer.class);

    @Value("${udp.server.port:9080}")
    int port;

    private static Map<String, LockProtocolHanderlIntf> lockProtocolHanderlIntfMap;

    Bootstrap bootstrap;
    private EventLoopGroup group;

    private Channel channel;

    /**
     * udp服务端，接受客户端发送的广播
     */
    @PostConstruct
    public void start() throws InterruptedException {
        this.group = new NioEventLoopGroup();
        this.bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioDatagramChannel.class)
                .option(ChannelOption.SO_BROADCAST, true)
                .handler(new UdpServerHandler());
        this.channel = bootstrap.bind(port).sync().channel();
        logger.info("UDP服务启动成功，端口：{}", port);
//        try {
//
//            channel.closeFuture().await();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            group.shutdownGracefully();
//        }
    }

    @PreDestroy
    public void stop() {
        logger.info("关闭UDP服务");
        if (null == channel) {
            logger.error("server channel is null");
        }
        group.shutdownGracefully();
        group = null;
        channel.closeFuture().syncUninterruptibly();
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.lockProtocolHanderlIntfMap = applicationContext.getBeansOfType(LockProtocolHanderlIntf.class);
    }

    private static class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {

            // 因为Netty对UDP进行了封装，所以接收到的是DatagramPacket对象。
            ByteBuf content = msg.content();
            content.readerIndex(0);
            int length = content.readableBytes();
            byte bts[] = new byte[length];
            content.getBytes(0, bts);
            try {
                byte [] head = new byte[2];
                byte [] command = new byte[1];
                content.getBytes(0, head, 0, 2);
                //当智能锁发的包是ef02代表是远程开锁的回复，所以没有指令码.
                if (!"ef02".equalsIgnoreCase(ByteUtils.bytes2hex(head))) {
                    content.getBytes(9, command, 0 , 1);
                }
                LockProtocolHanderlIntf protocolHandler = getProtocolHandler(head, command);
                if (protocolHandler != null) {
                    byte[] executeResult = protocolHandler.execute(content);
                    if (executeResult == null) {
                        throw new NullPointerException();
                    }
                    ctx.writeAndFlush(new DatagramPacket(
                            Unpooled.copiedBuffer(executeResult)
                            , msg.sender())).sync();
                } else {
                    // 没有能处理该消息的处理器
                    throw new NotSupportMethodHandlerException();
                }
            } catch (IndexOutOfBoundsException | NotSupportMethodHandlerException ex) {
                if (ex instanceof  NotSupportMethodHandlerException) {
                    logger.error("找不到消息处理器:{}", bytes2hex(bts));
                } else if (ex instanceof  IndexOutOfBoundsException){
                    logger.error("数据包格式错误:{}", bytes2hex(bts));
                } else if (ex instanceof ErrorParamException){
                    logger.error("参数错误:{}", bytes2hex(bts));
                }
            } catch (Exception ex) {
//                ex.printStackTrace();

                logger.error("信息处理返回null,指令码：{}， 异常：{}", new Object[]{bytes2hex(bts), ex});
            }


//            SpringContextHelper.getBean()

//            int length = subEventType.readableBytes();
//            byte bts[] = new byte[length];
//            subEventType.getBytes(0, bts);
////            subEventType.skipBytes(11);
//            System.out.println("===========");
//            System.out.println(subEventType.getByte(0));;
//            System.out.println(subEventType.getByte(1));;
//            int i = subEventType.readUnsignedShort();
//            System.out.println("数据" + i);
//            System.out.println(bytes2hex(bts));

//            byte[] bytes = msg.subEventType()。
//
//            // 因为Netty对UDP进行了封装，所以接收到的是DatagramPacket对象。
//            logger.info("数据包：", bytes2hex(bytes));
            //            GprsService service = SpringContextHelper.getBean(GprsService.class);
//            service.addData(data);

        }

        /**
         * 从所有的支持udp协议处理的处理处理其中，找出真实的处理器.
         * @param head
         * @param command
         * @return
         */
        private LockProtocolHanderlIntf getProtocolHandler(byte []head, byte[]command){
            Set<Map.Entry<String, LockProtocolHanderlIntf>> entries = lockProtocolHanderlIntfMap.entrySet();
            for (Map.Entry<String, LockProtocolHanderlIntf> entry : entries) {
                LockProtocolHanderlIntf protocolHander = entry.getValue();
                if (protocolHander.match(head, command)){
                    return protocolHander;
                }
            }
            return null;
        }
    }



    public static void main(String[] args) {
//        ByteBuf btf = new EmptyByteBuf();
        byte [] command = new byte[1];
        byte []actualCommand = ByteUtils.hexToBytes("00");
        if (Arrays.equals(command, actualCommand)){
            System.out.println("匹配成功");
        } else {
            System.out.println("匹配失败");
        }

    }

    public static String bytes2hex(byte[] bytes) {
        final String HEX = "0123456789abcdef";
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            // 取出这个字节的高4位，然后与0x0f与运算，得到一个0-15之间的数据，通过HEX.charAt(0-15)即为16进制数
            sb.append(HEX.charAt((b >> 4) & 0x0f));
            // 取出这个字节的低位，与0x0f与运算，得到一个0-15之间的数据，通过HEX.charAt(0-15)即为16进制数
            sb.append(HEX.charAt(b & 0x0f));
        }

        return sb.toString();
    }


}